{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from google.adk.agents.llm_agent import Agent\n",
    "from typing import Dict\n",
    "import sendgrid\n",
    "import os\n",
    "from sendgrid.helpers.mail import Mail, Email, To, Content\n",
    "import asyncio\n",
    "from google.adk.tools import agent_tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "gemini_api_key = os.getenv(\"GEMINI_API_KEY\")\n",
    "gemini_llm_model = os.getenv(\"GEMINI_LLM_MODEL\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "instructions1 = \"You are a sales agent working for ComplAI, \\\n",
    "a company that provides a SaaS tool for ensuring SOC2 compliance and preparing for audits, powered by AI. \\\n",
    "You write professional, serious cold emails.\"\n",
    "\n",
    "sales_agent1 = Agent(\n",
    "    name=\"Professional_Sales_Agent\",\n",
    "    model=gemini_llm_model,\n",
    "    description=(\"You write professional, serious cold emails\"),\n",
    "    instruction=(instructions1)\n",
    ")\n",
    "sales_agent1_tool = agent_tool.AgentTool(agent=sales_agent1) # Wrap the agent\n",
    "\n",
    "instructions2 = \"You are a humorous, engaging sales agent working for ComplAI, \\\n",
    "a company that provides a SaaS tool for ensuring SOC2 compliance and preparing for audits, powered by AI. \\\n",
    "You write witty, engaging cold emails that are likely to get a response.\"\n",
    "\n",
    "sales_agent2 = Agent(\n",
    "        name=\"Engaging_Sales_Agent\",\n",
    "        model=gemini_llm_model,\n",
    "        description=(\"You write witty, engaging cold emails that are likely to get a response\"),\n",
    "        instruction=(instructions2)\n",
    ")\n",
    "sales_agent2_tool = agent_tool.AgentTool(agent=sales_agent2) # Wrap the agent\n",
    "\n",
    "instructions3 = \"You are a busy sales agent working for ComplAI, \\\n",
    "a company that provides a SaaS tool for ensuring SOC2 compliance and preparing for audits, powered by AI. \\\n",
    "You write concise, to the point cold emails.\"\n",
    "sales_agent3 = Agent(\n",
    "        name=\"Busy_Sales_Agent\",\n",
    "        model=gemini_llm_model,\n",
    "        description=(\"You write concise, to the point cold emails.\"),\n",
    "        instruction=(instructions3)\n",
    ")\n",
    "sales_agent3_tool = agent_tool.AgentTool(agent=sales_agent3) # Wrap the agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "best_email_picker_instructions=\"You pick the best cold sales email from the given options. \\\n",
    "Imagine you are a customer and pick the one you are most likely to respond to. \\\n",
    "Do not give an explanation; reply with the selected email only.\"\n",
    "\n",
    "best_email_picker = Agent(\n",
    "    name=\"sales_picker\",\n",
    "    description=(\"You pick the best cold sales email from a set of provided emails\"),\n",
    "    instruction=(best_email_picker_instructions),\n",
    "    model=gemini_llm_model,\n",
    ")\n",
    "\n",
    "best_email_picker_agent_tool = agent_tool.AgentTool(agent=best_email_picker)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "subject_instructions = \"You can write a subject for a cold sales email. \\\n",
    "You are given a message and you need to write a subject for an email that is likely to get a response.\"\n",
    "\n",
    "subject_writer = Agent(\n",
    "    name=\"Email_subject_writer\", \n",
    "    description=(\"You write the subject of the email based on the email body provided\"),\n",
    "    instruction=(subject_instructions), \n",
    "    model=gemini_llm_model\n",
    "    )\n",
    "\n",
    "subject_writer_agent_tool = agent_tool.AgentTool(agent=subject_writer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "html_instructions = \"You can convert a text email body to an HTML email body. \\\n",
    "You are given a text email body which might have some markdown \\\n",
    "and you need to convert it to an HTML email body with simple, clear, compelling layout and design.\"\n",
    "\n",
    "html_converter = Agent(\n",
    "    name=\"HTML_email_body_converter\", \n",
    "    description=(\"You convert the email into HTML format\"),\n",
    "    instruction=(html_instructions), \n",
    "    model=gemini_llm_model\n",
    "    )\n",
    "\n",
    "html_converter_agent_tool = agent_tool.AgentTool(agent=html_converter)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def send_html_email(subject: str, html_body: str) -> Dict[str, str]:\n",
    "    \"\"\" Send out an email with the given subject and HTML body to all sales prospects \"\"\"\n",
    "    sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY'))\n",
    "    from_email = os.getenv(\"FROM_EMAIL\")\n",
    "    to_email = os.getenv(\"TO_EMAIL\")\n",
    "    from_email = Email(from_email)  # Change to your verified sender\n",
    "    to_email = To(to_email)  # Change to your recipient\n",
    "    content = Content(\"text/html\", html_body)\n",
    "    mail = Mail(from_email, to_email, subject, content).get()\n",
    "    sg.client.mail.send.post(request_body=mail)\n",
    "    return {\"status\": \"success\"}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<google.adk.tools.agent_tool.AgentTool at 0x164176d20>,\n",
       " <google.adk.tools.agent_tool.AgentTool at 0x1641747d0>,\n",
       " <google.adk.tools.agent_tool.AgentTool at 0x1641769f0>,\n",
       " <google.adk.tools.agent_tool.AgentTool at 0x164322d20>,\n",
       " <google.adk.tools.agent_tool.AgentTool at 0x164321160>,\n",
       " <google.adk.tools.agent_tool.AgentTool at 0x164322bd0>,\n",
       " <function __main__.send_html_email(subject: str, html_body: str) -> Dict[str, str]>]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tools = [\n",
    "    sales_agent1_tool, \n",
    "    sales_agent2_tool, \n",
    "    sales_agent3_tool, \n",
    "    best_email_picker_agent_tool,\n",
    "    subject_writer_agent_tool,\n",
    "    html_converter_agent_tool,\n",
    "    send_html_email]\n",
    "\n",
    "tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Improved instructions thanks to student Guillermo F.\n",
    "\n",
    "sales_manager_instructions = \"\"\"\n",
    "You are a Sales Manager at ComplAI. Your goal is to find the single best cold sales email using the sales_agent tools.\n",
    " \n",
    "Follow these steps carefully:\n",
    "1. Generate Drafts: Use all three sales_agent tools to generate three different email drafts. Do not proceed until all three drafts are ready.\n",
    " \n",
    "2. Evaluate and Select: Use the email_picker_agent_tool to Review the drafts and choose the single best email using your judgment of which one is most effective.\n",
    "\n",
    "3. Create the subject line: Use the subject_writer_agent_tool to write the subject of the email chosen.\n",
    "\n",
    "4. Convert to html email format: Use the html_converter_agent_tool to convert the email to html format\n",
    " \n",
    "5. Use the send_html_email tool to send the best email (and only the best email) to the user.\n",
    " \n",
    "Crucial Rules:\n",
    "- You must use the sales agent tools to generate the drafts — do not write them yourself.\n",
    "- You must send ONE email using the send_email tool — never more than one.\n",
    "\"\"\"\n",
    "\n",
    "sales_manager = Agent(\n",
    "    name=\"Sales_Manager\", \n",
    "    instruction=(sales_manager_instructions), \n",
    "    description=(\"Your goal is to find the single best cold sales email using the sales_agent tools, pick the best email, get the subject of the email, convert to html format and send the email\"),\n",
    "    tools=tools, \n",
    "    model=gemini_llm_model,\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Session created: App='Cold_Email_App', User='user_1', Session='session_001'\n"
     ]
    }
   ],
   "source": [
    "from google.adk.sessions import InMemorySessionService\n",
    "from google.adk.runners import Runner\n",
    "\n",
    "session_service = InMemorySessionService()\n",
    "APP_NAME = \"Cold_Email_App\"\n",
    "USER_ID = \"user_1\"\n",
    "SESSION_ID = \"session_001\" # Using a fixed ID for simplicity\n",
    "\n",
    "# Create the specific session where the conversation will happen\n",
    "session = await session_service.create_session(\n",
    "    app_name=APP_NAME,\n",
    "    user_id=USER_ID,\n",
    "    session_id=SESSION_ID\n",
    ")\n",
    "print(f\"Session created: App='{APP_NAME}', User='{USER_ID}', Session='{SESSION_ID}'\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Runner created for agent 'Sales_Manager'.\n"
     ]
    }
   ],
   "source": [
    "# --- Runner ---\n",
    "# Key Concept: Runner orchestrates the agent execution loop.\n",
    "runner = Runner(\n",
    "    agent=sales_manager, # The agent we want to run\n",
    "    app_name=APP_NAME,   # Associates runs with our app\n",
    "    session_service=session_service # Uses our session manager\n",
    ")\n",
    "print(f\"Runner created for agent '{runner.agent.name}'.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @title Define Agent Interaction Function\n",
    "\n",
    "from google.genai import types # For creating message Content/Parts\n",
    "\n",
    "async def call_agent_async(query: str, runner, user_id, session_id):\n",
    "  \"\"\"Sends a query to the agent and prints the final response.\"\"\"\n",
    "  print(f\"\\n>>> User Query: {query}\")\n",
    "\n",
    "  # Prepare the user's message in ADK format\n",
    "  content = types.Content(role='user', parts=[types.Part(text=query)])\n",
    "\n",
    "  final_response_text = \"Agent did not produce a final response.\" # Default\n",
    "\n",
    "  # Key Concept: run_async executes the agent logic and yields Events.\n",
    "  # We iterate through events to find the final answer.\n",
    "  async for event in runner.run_async(user_id=user_id, session_id=session_id, new_message=content):\n",
    "      # You can uncomment the line below to see *all* events during execution\n",
    "      print(f\"  [Event] Author: {event.author}, Type: {type(event).__name__}, Final: {event.is_final_response()}, Content: {event.content}\")\n",
    "\n",
    "      # Key Concept: is_final_response() marks the concluding message for the turn.\n",
    "      if event.is_final_response():\n",
    "          if event.content and event.content.parts:\n",
    "             # Assuming text response in the first part\n",
    "             final_response_text = event.content.parts[0].text\n",
    "          elif event.actions and event.actions.escalate: # Handle potential errors/escalations\n",
    "             final_response_text = f\"Agent escalated: {event.error_message or 'No specific message.'}\"\n",
    "          # Add more checks here if needed (e.g., specific error codes)\n",
    "          break # Stop processing events once the final response is found\n",
    "\n",
    "  print(f\"<<< Agent Response: {final_response_text}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "async def run_conversation():\n",
    "    message = \"Send out a cold sales email addressed to Dear CEO from Alice\"\n",
    "    await call_agent_async(\n",
    "        message,\n",
    "        runner=runner,\n",
    "        user_id=USER_ID,\n",
    "        session_id=SESSION_ID\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "await run_conversation()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venvgoogle",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
